#include <iostream>
#include <ceres/ceres.h>
#include <chrono>
#include <opencv2/core/core.hpp>
#include <vector>

using namespace std;
using namespace ceres;

struct CURVE_FITTING_COST{
    CURVE_FITTING_COST(double x,double y):_x(x),_y(y){}
    
    template <typename T>
    bool operator()(const T* const abc, T* residual) const{
        
        residual[0] = T(_y)-ceres::exp(abc[0]*T(_x)*T(_x)+abc[1]*T(_x)+abc[2]);
        return true;
    }
    
    const double _x,_y; 
};

int main(int argc, char **argv) {
    
    double a = 1.0,b=2.0,c=1.0;
    int N = 100;
    double w_sigma =1.0;
    cv::RNG rng;
    
    double abc[3] = {0,0,0};
    vector<double> xData,yData;
    
    cout<<"generating data"<<endl;
    
    for(int i=0;i<N;i++){
        double x = i/100.0;
        xData.push_back(x);
        yData.push_back(ceres::exp(a*x*x+b*x+c)+rng.gaussian(w_sigma));
        
        cout<<xData[i]<<"  "<<yData[i]<<endl;
    }
    
    Problem problem;
    for(int i=0;i<N;i++){
        
        problem.AddResidualBlock(new ceres::AutoDiffCostFunction<CURVE_FITTING_COST,1,3>(
            new CURVE_FITTING_COST( xData[i],yData[i])
        ),
        nullptr,
        abc
        );
    }
    ceres::Solver::Options options;
    options.linear_solver_type = ceres::DENSE_QR;
    options.minimizer_progress_to_stdout = true;
    
    ceres::Solver::Summary summary;
    
    ceres::Solve (options,&problem,&summary);
    
    for(auto a:abc){
        cout<<a<<"  ";
        
    }
    
    std::cout << "Hello, world!" << std::endl;
    return 0;
}
